package gateway

import (
	"common/response"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"regexp"

	"github.com/gogf/gf/v2/util/gconv"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
)

func GrpcHandler(
	ctx context.Context,
	//grpcEndpoint string,
	middlewares []func(http.HandlerFunc) http.HandlerFunc,
	Helpers []HeperFace,
	// registerFuncs ...func(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error),
) (http.Handler, error) {
	mux := runtime.NewServeMux(
		// runtime.WithXxx方法可对请求的解析做一些设置
		// runtime.WithIncomingHeaderMatcher(runtime.DefaultHeaderMatcher),
		runtime.SetQueryParameterParser(&CustomQueryParser{}),
		runtime.WithErrorHandler(CustomErrorHandler),
		runtime.WithMarshalerOption(runtime.MIMEWildcard, &CustomMarshaler{
			JSONPb: runtime.JSONPb{
				MarshalOptions: protojson.MarshalOptions{
					EmitUnpopulated: true,
				},
				UnmarshalOptions: protojson.UnmarshalOptions{
					DiscardUnknown: true,
				},
			},
		}),
	)
	opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}

	for _, Helper := range Helpers {
		grpcEndpoint := Helper.GrpcEndpoint
		for _, reg := range Helper.RegisterFuncs {
			err := reg(ctx, mux, grpcEndpoint, opts)
			if err != nil {
				return nil, err
			}
		}
	}
	//for _, reg := range registerFuncs {
	//	err := reg(ctx, mux, grpcEndpoint, opts)
	//	if err != nil {
	//		return nil, err
	//	}
	//}
	return withMiddlewares(mux, middlewares...), nil
}

func withMiddlewares(handler http.Handler, middlewares ...func(http.HandlerFunc) http.HandlerFunc) http.Handler {
	if len(middlewares) == 0 {
		return handler
	}
	var hf http.HandlerFunc
	hf = handler.ServeHTTP
	for _, m := range middlewares {
		hf = m(hf)
	}
	return hf
}

func CustomErrorHandler(ctx context.Context, serveMux *runtime.ServeMux, m runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) {
	if err == nil {
		return
	}
	response.GrpcGatewayResponse(r, w, nil, err)
}

type CustomMarshaler struct {
	runtime.JSONPb
}

func (c *CustomMarshaler) Marshal(v interface{}) (body []byte, err error) {

	r, err := c.JSONPb.Marshal(v)
	if err != nil {
		return
	}
	// 不直接用结构体是因为外面包一层以后，里面的空置不会被展示出来
	// {
	//    "code": 0,
	//    "message": "OK",
	//    "data": {
	//        "detail": {
	//            "id": 28,
	//            "name": "333",
	//            "structureId": 362
	//        },
	//        "managers": []   //包一层以后，这个值就展示不出来
	//    }
	//}
	body = []byte(fmt.Sprintf(`{"code":0,"message":"OK","data":%s}`, string(r)))

	return
}

type CustomQueryParser struct {
	runtime.DefaultQueryParser
}

func (c *CustomQueryParser) Parse(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
	//自定义的目的是为了兼容gozero的 SB query 解析方式, 目前只处理了数组的情况
	array := `^\s*\[(.*)\]\s*$`
	re := regexp.MustCompile(array)
	for key, vals := range values {
		if re.MatchString(vals[0]) {
			var tmp any
			err := json.Unmarshal([]byte(vals[0]), &tmp)
			if err != nil {
				return err
			}
			values[key] = gconv.SliceStr(tmp)
		}
	}
	return c.DefaultQueryParser.Parse(msg, values, filter)
}
